
using System.Drawing;
using Xeml.Document;
using System.Collections.Generic;
using System;
using Mpi.Common.Collections;
using Xeml.Document.CoreObjects;
using System.Diagnostics;

namespace Xeml.Visualization
{
    public class VisStory : VisualTimeElement
    {
        #region Fields
        private TreeNode<StoryBase> story;
        private VisStory parent;
        private List<VisObserverPoint> visObPoints;
        private SizeF opSize = new SizeF(14f, 14f);
        private VisualXemlDoc visualDoc;
        private List<VisEvent> _visEvents;

        public List<VisEvent> VisEvents
        {
            get { return _visEvents; }
        }


        #endregion

        #region Properties
        public override System.TimeSpan Time
        {
            get
            {
                if (this.story.Content is Story)
                    return TimeSpan.Zero;
                else if (this.story.Content is StorySplit)
                {
                    return (this.story.Content as StorySplit).TimePoint;
                }
                else
                    return TimeSpan.Zero;
            }
            set
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
        }



        public List<VisObserverPoint> VisObPoints
        {
            get { return visObPoints; }
        }


        public VisStory Parent
        {
            get { return parent; }
        }

        public TreeNode<StoryBase> Story
        {
            get { return story; }
        }
        #endregion

        public delegate void MessageEmittedEventHandler(object sender, MessageEventArgs e);
        public event MessageEmittedEventHandler MessageEmitted;
 
        public void StatusUpdate(string message)
        {
            if (MessageEmitted != null)
            {
                MessageEmitted(this, new MessageEventArgs(message, EventLogEntryType.Information));
            }
        } 

        #region Constructors
        public VisStory(TreeNode<StoryBase> story, VisStory parent, VisualXemlDoc visualDoc, float x, float y, SizeF size)
        {
            StatusUpdate("Constructing.");
            this.visualDoc = visualDoc;
            if (story.Content is StorySplit)
            {
                StatusUpdate("Registering TimeChanged Event.");
                (story.Content as StorySplit).TimePointHasChanged += new EventHandler<Mpi.Common.PropertyHasChangedEventArgs<TimeSpan>>(VisStory_TimePointChanged);
            }
            this.visObPoints = new List<VisObserverPoint>();
            _visEvents = new List<VisEvent>();
            this.story = story;
            this.parent = parent;
            this.x = x;
            this.y = y;
            this.Size = size;
            InitVisObPoints();
            InitVisEvents();
        }

        void VisStory_TimePointChanged(object sender, Mpi.Common.PropertyHasChangedEventArgs<TimeSpan> e)
        {
            LinkedListNode<TimeRegion> trN = this.visualDoc.PxMapper.Regions.First;
            while (trN != null && trN.Value.End != e.OldValue)
            {
                trN = trN.Next;
            }

            if (trN != null)
            {
                TimeSpan diff = e.NewValue - e.OldValue;
                this.visualDoc.PxMapper.Regions.SuspendChangeEvent();
                trN.Value.Length += diff;
                trN.Next.Value.Length -= diff;
                this.visualDoc.PxMapper.Regions.ResumeChangeEvent(false);
            }
            base.OnXLocationUpdateNeeded(e);
        }
        #endregion

        #region Methods

        //public override bool Selected
        //{
        //    get
        //    {
        //        return base.Selected;
        //    }
        //    set
        //    {
        //        if (selected != value)
        //        {
        //            selected = value;
        //            if(selected)
        //                OnSelection();
        //        }
        //    }
        //}

        public override void TrySelect(int x, int y, bool multiSelect)
        {
            if (multiSelect)
            {
                if (this.shape.IsVisible(x, y))
                {
                    if (this.visualDoc.SelectedStories.Length > 1 || !this.Selected)
                    {
                        this.Selected = !this.Selected;
                    }
                    OnTouched();
                }
            }
            else
            {
                if (this.shape.IsVisible(x, y))
                {
                    this.Selected = true;
                    OnTouched();
                }
                else if(visualDoc.SelectedStories.Length > 1)
                    this.Selected = false;
            }
        }
        private void InitVisObPoints()
        {
            this.visObPoints.Clear();
            foreach (ObservationPoint op in this.story.Content.ObservationPoints)
            {
                AddVisObPoint(op);
            }

            this.story.Content.ObservationPoints.ItemAdded += new EventHandler<ItemEventArgs<ObservationPoint>>(OnObservationPointAdded);
            this.story.Content.ObservationPoints.ItemRemoved += new EventHandler<ItemEventArgs<ObservationPoint>>(OnObservationPointRemoved);
        }

        private void InitVisEvents()
        {
            this._visEvents.Clear();
            foreach (Event ev in this.story.Content.Events)
            {
                AddVisEvent(ev);
            }

            this.story.Content.Events.ItemAdded += new EventHandler<ItemEventArgs<Event>>(OnEventAdded);
            this.story.Content.Events.ItemRemoved += new EventHandler<ItemEventArgs<Event>>(OnEventRemoved);
        }

        void OnEventRemoved(object sender, ItemEventArgs<Event> e)
        {
            VisEvent toRemove = null;
            foreach (VisEvent vop in this._visEvents)
            {
                if (vop.Event == e.Item)
                {
                    //vop.Selected = false;
                    toRemove = vop;
                    break;
                }
            }
            if (toRemove != null)
            {
                toRemove.IsHighLighted = false;
                toRemove.Selected = false;
                toRemove.HighLightChanged -= new EventHandler(this.visualDoc.OnHighLightedObjectChanged);
                toRemove.Selection -= new EventHandler(this.visualDoc.OnVisEventSelectionChanged);
                toRemove.Touched -= new EventHandler(this.visualDoc.OnTouched);
                this._visEvents.Remove(toRemove);
                this.visualDoc.CalculateEvents();
                this.visualDoc.OnInvalidated();
            }
        }

        void OnEventAdded(object sender, ItemEventArgs<Event> e)
        {
            AddVisEvent(e.Item);
        }

        private void AddVisEvent(Event ev)
        {
            float y = this.Y;
            float x = (float)visualDoc.PxMapper.MapPoint(ev.TimePoint);
            //RectangleF bounds = CalcVisOpPointBounds(op);
            VisEvent ve = new VisEvent(ev, x, y, opSize);
            this._visEvents.Add(ve);
            ve.XLocationUpdateNeeded += new EventHandler<Mpi.Common.PropertyHasChangedEventArgs<TimeSpan>>(OnVopXLocationUpdateNeeded);
            ve.HighLightChanged += new EventHandler(this.visualDoc.OnHighLightedObjectChanged);
            ve.Selection += new EventHandler(this.visualDoc.OnVisEventSelectionChanged);
            ve.Touched += new EventHandler(this.visualDoc.OnTouched);
            this.visualDoc.CalculateEvents();
        }

        private void AddVisObPoint(ObservationPoint op)
        {
            StatusUpdate("Adding ObPoint.");
            float y = this.Y;
            float x = (float)visualDoc.PxMapper.MapPoint(op.TimePoint);
            //RectangleF bounds = CalcVisOpPointBounds(op);
            VisObserverPoint vop = new VisObserverPoint(op, x, y, opSize);
            this.visObPoints.Add(vop);
            vop.XLocationUpdateNeeded += new EventHandler<Mpi.Common.PropertyHasChangedEventArgs<TimeSpan>>(OnVopXLocationUpdateNeeded);
            vop.HighLightChanged += new EventHandler(this.visualDoc.OnHighLightedObjectChanged);
            vop.Selection += new EventHandler(this.visualDoc.OnVisObserverPointSelectionChanged);
            vop.Touched += new EventHandler(this.visualDoc.OnTouched);
            this.visualDoc.CalculateObserverPoints();
        }

        protected override void SetShape()
        {
            RectangleF rect = new RectangleF();
            rect.X = this.X;
            rect.Y = this.Y - (this.Size.Height / 2f);
            rect.Size = this.Size;
            this.shape = new System.Drawing.Drawing2D.GraphicsPath();
            this.shape.AddRectangle(rect);
        }
        #endregion

        #region Event-/handling

        /// <summary>
        /// Execute necessary action if a observation point needs a loction update.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnVopXLocationUpdateNeeded(object sender, Mpi.Common.PropertyHasChangedEventArgs<TimeSpan> e)
        {
            VisualTimeElement vop = sender as VisualTimeElement;
            int px = visualDoc.PxMapper.MapPoint(vop.Time);
           // int oldpx = visualDoc.PxMapper.MapPoint(e.OldValue);


            float y = this.Y;
            float x = (float)px;

            //px = visualDoc.PxMapper.LogicPixelAddressOf(px);
            //oldpx = visualDoc.PxMapper.LogicPixelAddressOf(oldpx);
            //this.visualDoc.HotSpots.Remove(oldpx);
            //if (!this.visualDoc.HotSpots.Contains(px))
            //    this.visualDoc.HotSpots.Add(px);

            vop.Y = y;
            vop.X = x;
            this.visualDoc.OnInvalidated();
        }


        //void OnVopLocationUpdateNeeded(object sender, System.EventArgs e)
        //{

        //    //vop.SetBounds(CalcVisOpPointBounds(vop.ObservationPoint));
        //}

        /// <summary>
        /// Handler for removed ob points.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnObservationPointRemoved(object sender, ItemEventArgs<ObservationPoint> e)
        {
            VisObserverPoint toRemove = null;
            foreach (VisObserverPoint vop in this.visObPoints)
            {
                if (vop.ObservationPoint == e.Item)
                {
                    //vop.Selected = false;
                    toRemove = vop;
                    break;
                }
            }
            if (toRemove != null)
            {
                toRemove.IsHighLighted = false;
                toRemove.Selected = false;
                toRemove.XLocationUpdateNeeded -= new EventHandler<Mpi.Common.PropertyHasChangedEventArgs<TimeSpan>>(OnVopXLocationUpdateNeeded);
                toRemove.HighLightChanged -= new EventHandler(this.visualDoc.OnHighLightedObjectChanged);
                toRemove.Selection -= new EventHandler(this.visualDoc.OnVisObserverPointSelectionChanged);
                toRemove.Touched -= new EventHandler(this.visualDoc.OnTouched);
                this.visObPoints.Remove(toRemove);
                this.visualDoc.CalculateObserverPoints();
                this.visualDoc.OnInvalidated();
            }
        }

        /// <summary>
        /// Handler for added ob points.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnObservationPointAdded(object sender, ItemEventArgs<ObservationPoint> e)
        {
            AddVisObPoint(e.Item);
            // this.visualDoc.OnInvalidated();
        }
        #endregion
    }
}